package game.poker.holdem.entity;

import game.poker.holdem.card.CommunityCards;
import game.poker.holdem.hand.BestHandExpert;
import game.poker.holdem.hand.Hand;
import game.poker.holdem.hand.Ranking;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Game {    
    private static int tableNumber = 0;
    
    private final Map<String, Table> tables;
    
    private List<Player> winners;
    
    public Game() {        
        tables = new HashMap<String, Table>();
    }
    
    public void doDistributionRound(String tableId) {
        Table table = tables.get(tableId);
        Dealer dealer = table.getDealer();
        dealer.distribute();
    }
    
    public void doFlopRound(String tableId) {
        Table table = tables.get(tableId);
        Dealer dealer = table.getDealer();
        dealer.flop();
    }
    
    public void doTurnRound(String tableId) {
        Table table = tables.get(tableId);
        Dealer dealer = table.getDealer();
        dealer.turn();
    }
    
    public void doRiverRound(String tableId) {
        Table table = tables.get(tableId);
        Dealer dealer = table.getDealer();
        dealer.river();
    }
    
    public void doShowdown(String tableId) {
        Table table = tables.get(tableId);
        CommunityCards commCards = table.getCommunityCards();
        BestHandExpert bhe = new BestHandExpert(commCards);
        List<Player> players = table.getPlayers();
        for(Player player : players) {
            bhe.determineBestHandFor(player);
        }
        
        Map<Ranking, List<Player>> rankingPlayersMap = new HashMap<Ranking, List<Player>>();
        for(Player player : players) {
        	Ranking ranking = player.getBestHand().getRanking();
        	List<Player> rankingPlayers = rankingPlayersMap.get(ranking);
        	if(rankingPlayers == null) {
        		rankingPlayers = new ArrayList<Player>();
        		rankingPlayersMap.put(ranking, rankingPlayers);
        	}
        	rankingPlayers.add(player);
        }
        List<Ranking> rankings = new ArrayList<Ranking>(rankingPlayersMap.keySet());
        Collections.sort(rankings);
        System.out.println("Rankings: " + rankings);
        Ranking bestRanking = rankings.get(0);      
        System.out.println("Best ranking: " + bestRanking);
        List<Player> bestRankingPlayers = rankingPlayersMap.get(bestRanking);
        System.out.println("Best ranking players: " + bestRankingPlayers);
        Map<Long, List<Player>> scorePlayersMap = new HashMap<Long, List<Player>>();
        for(Player player : bestRankingPlayers) {
        	Long score = player.getBestHand().getScore();
        	List<Player> scorePlayers = scorePlayersMap.get(score);
        	if(scorePlayers == null) {
        		scorePlayers = new ArrayList<Player>();
        		scorePlayersMap.put(score, scorePlayers);
        	}
        	scorePlayers.add(player);
        }
        List<Long> scores = new ArrayList<Long>(scorePlayersMap.keySet());
        Collections.sort(scores);
        Long bestScore = scores.get(scores.size() - 1);
        System.out.println("Best score: " + bestScore);
        List<Player> bestScorePlayers = scorePlayersMap.get(bestScore);
        System.out.println("Best score players: " + bestScorePlayers);
        winners = bestScorePlayers;
    }
    
    public void printResult(String tableId) {
    	System.out.println();
        Table table = tables.get(tableId);
        CommunityCards commCards = table.getCommunityCards();
        List<Player> players = table.getPlayers();
        for(Player player : players) {
        	Hand bestHand = player.getBestHand();
        	StringBuilder sb = new StringBuilder(player.getName()).append(":  ")
        														   .append(commCards.toStringUnicodeImages())
        														   .append("  ")
        														   .append(player.holeCardsToStringUnicodeImages())
        														   .append(" => ")
        														   .append(bestHand.handCardsToStringUnicodeImages())
        														   .append(" => ")
        														   .append(bestHand.getRanking())
        														   .append(" (")
        														   .append(bestHand.getScore())
        														   .append(")");
            System.out.printf("%s%n%n", sb);            
        }
        System.out.println("Winners: " + winners);
    }
        
    public boolean join(String tableId, Player player) {
        Table table = tables.get(tableId);
        return table.addPlayer(player);
    }
    
    public String findOrNewTable(Player player) {
        for(Map.Entry<String, Table> tableE : tables.entrySet()) {
            Table table = tableE.getValue();
            if(table.isSeatAvailable()) {
                String tableId = tableE.getKey();
                join(tableId, player);
                return tableId;
            }
        }
        
        tableNumber++;
        String tableId = "table_" + tableNumber;
        tables.put(tableId, new Table(new Dealer("Dealer " + tableNumber)));
        join(tableId, player);
        return tableId;
    }
    
    public static void main(String[] args) {
        Game game = new Game();
        game.findOrNewTable(new Player("Jimmy"));
        game.findOrNewTable(new Player("Jon"));
        game.findOrNewTable(new Player("Jack"));
        game.findOrNewTable(new Player("Joe"));
        String tableId = game.findOrNewTable(new Player("Player 5"));
        game.doDistributionRound(tableId);
        game.doFlopRound(tableId);
        game.doTurnRound(tableId);
        game.doRiverRound(tableId);
        game.doShowdown(tableId);
        game.printResult(tableId);
    }
}
